﻿namespace DotNetUtility.ExtensionMethods
{
    using System;
    using System.Text;
    using System.Collections.Generic;
    using DotNetUtility.GenericDesignPattern;

    public static class StringExtensions
    {
        /// <summary>
        /// To join with another string item.
        /// </summary>
        /// <param name="baseItem"></param>
        /// <param name="itemToJoin"></param>
        /// <returns></returns>
        /// <example>
        ///     <code>
        ///         string baseString ="BaseString";
        ///         string result = baseString.JoinWith("One");        
        ///     </code>
        /// </example>
        /// Contributed by Mohammad Rahman
        public static string JoinWith(this string baseItem, string itemToJoin)
        {
            return string.Concat(baseItem, itemToJoin);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseItem"></param>
        /// <param name="firstItem"></param>
        /// <param name="secondItem"></param>
        /// <returns></returns>
        /// <example>
        ///     <code>
        ///         string baseString ="BaseString";
        ///         string result = baseString.JoinWith("One","Two");
        ///     </code>
        /// </example>
        /// Contributed by Mohammad Rahman
        public static string JoinWith
            (this string baseItem, string firstItem, string secondItem)
        {
            return string.Concat(values: new[] { baseItem, firstItem, secondItem });
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseItem"></param>
        /// <param name="itemToJoin"></param>
        /// <param name="separator"></param>
        /// <returns></returns>
        /// <example>
        ///     <code>
        ///         string baseString ="BaseString";
        ///         string result = baseString.JoinWithSeparator("One",",");
        ///     </code>
        /// </example>
        /// Contributed by Mohammad Rahman
        public static string JoinWithSeparator
            (this string baseItem, string itemToJoin, string separator = default(string))
        {
            return string.Join(separator: separator, value: new[] { baseItem, itemToJoin });
        }

        /// <summary>
        /// To join with two string items.
        /// </summary>
        /// <param name="baseItem">Item to join with</param>
        /// <param name="firstItem"></param>
        /// <param name="secondItem"></param>
        /// <param name="separator"></param>
        /// <returns>Joined string.</returns>
        /// <example>
        ///     <code>
        ///         string baseString ="BaseString";
        ///         string result = baseString.JoinWithSeparator("One", "Two",",");
        ///     </code>
        /// </example>
        /// Contributed by Mohammad Rahman
        public static string JoinWithSeparator
            (this string baseItem, string firstItem, string secondItem, string separator = default(string))
        {
            return string.Join(separator: separator, value: new[] { baseItem, firstItem, secondItem });
        }

        /// <summary>
        /// To join with an array.
        /// </summary>
        /// <param name="baseItem">Item to join with.</param>
        /// <param name="itemsToJoin">Array to join.</param>
        /// <param name="separator"></param>
        /// <returns>Joined string.</returns>
        /// <example>
        ///     <code>
        ///         string baseString ="BaseString";
        ///         string result = baseString.JoinWithSeparator( new[]{"One", "Two"} );
        ///     </code>
        /// </example>
        /// Contributed by Mohammad Rahman
        public static string JoinWithSeparator
            (this string baseItem, string[] itemsToJoin, string separator = default(string))
        {
            var builder = new StringBuilder();

            Array.ForEach(itemsToJoin,
                item =>
                {
                    builder.Append(value: item).Append(separator);
                });

            return ReferenceEquals(separator, default(string)) ? builder.ToString() : builder.ToString().TrimEnd(separator.ToCharArray());
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <typeparam name="T3"></typeparam>
        /// <param name="baseItem"></param>
        /// <param name="tupleOfJoinableItems"></param>
        /// <returns></returns>
        /// <example>
        ///     <code>
        ///     </code>
        /// </example>
        /// Contributed by Mohammad Rahman
        public static string JoinWithDifferentTypes<T1, T2, T3>
            (this string baseItem, Tuple<T1, T2, T3> tupleOfJoinableItems)
            where T1 : struct
            where T2 : struct
            where T3 : class
        {
            var builder = new StringBuilder();
            builder.Append(baseItem).Append(tupleOfJoinableItems.Item1).Append(tupleOfJoinableItems.Item2);
            return ReferenceEquals(tupleOfJoinableItems.Item3, null) ? builder.ToString() : builder.Append(tupleOfJoinableItems.Item3.ToString()).ToString();
        }

        /// <summary>
        /// To check if the string has default value or it is null.
        /// </summary>
        /// <param name="itemToCheck">The item to check</param>
        /// <returns>true or false</returns>
        /// <example>
        ///     <code>
        ///         string myString = default(string);
        ///         var result = myString.IsNull();
        ///     </code>
        /// </example>  
        /// Contributed by Mohammad Rahman
        public static bool IsNull(this string itemToCheck)
        {
            return itemToCheck == default(string);
        }

        /// <summary>
        ///  To convert into boolean otherwise return null.
        /// </summary>
        /// <param name="booleanString"></param>
        /// <returns></returns>
        /// <example>
        ///     <code>
        ///         strin itemToText="true";
        ///         object result= itemToText.ToBoolean();
        ///     </code>
        /// </example>
        /// Contributed by Mohammad Rahman
        public static Nullable<bool> ToBoolean(this string booleanString)
        {
            bool result;
            if (Boolean.TryParse(booleanString, out result))
                return result;
            return null;
        }

        /// <summary>
        /// Property which will hold the selection logic for StrategyOfSelectionMethod pattern which will used by ToBooleanFromAnyText()
        /// </summary>
        /// Contributed by Mohammad Rahman
        private static StrategyOfMethodSelection<BooleanEquivalent> strategyOfBooleanMethodSelection = new StrategyOfMethodSelection<BooleanEquivalent>(
                new Dictionary<BooleanEquivalent, Func<bool>>()
                {
                    { BooleanEquivalent.One, ()=> {return true;} },
                    { BooleanEquivalent.Zero,()=>{return false;} },
                    { BooleanEquivalent.Yes, ()=> {return true;} },
                    { BooleanEquivalent.No, ()=> {return false;} },
                });

        /// <summary>
        /// To convert text into boolean text for example One , Zero or Yes or No and so on; otherwise return the given text.
        /// </summary>
        /// <param name="anyText"></param>
        /// <returns></returns>
        /// <example>
        ///     <code>
        ///         string anyText ="anyText";
        ///         string result = anyText.ConvertToBooleanTextIfPossible();
        ///     </code>
        /// </example>
        /// Contributed by Mohammad Rahman
        public static string ConvertToBooleanTextIfPossible(this string anyText)
        {
            return
                anyText == "0" ? "Zero" : anyText == "1" ? "One" :
                anyText.ToLower() == "yes" ? "Yes" : anyText.ToLower() == "no" ? "No" :
                anyText.ToLower() == "zero" ? "Zero" : anyText.ToLower() == "one" ? "One" : anyText;
        }

        /// <summary>
        /// To convert into Boolean from any text otherwise return null.
        /// </summary>
        /// <param name="booleanString">Text to convert into Boolean.</param>
        /// <returns></returns>
        /// <example>
        ///     <code>
        ///         string itemToTest ="AnyText";
        ///         object result = itemToTest.ToBooleanFromAnyText();
        ///     </code>
        /// </example>
        /// Contributed by Mohammad Rahman
        public static Nullable<bool> ToBooleanFromAnyText(this string booleanString)
        {
            bool result;
            BooleanEquivalent booleanEquivalent;
            booleanString = booleanString.ConvertToBooleanTextIfPossible();

            if (booleanString.IsItemInEnum<BooleanEquivalent>())
            {
                booleanEquivalent = booleanString.ParseStringToEnum<BooleanEquivalent>(ignorecase: true);
                if (booleanEquivalent == default(BooleanEquivalent))
                    return null;
                else
                    return strategyOfBooleanMethodSelection.ExecuteFuncWithBool(booleanEquivalent);
            }
            else
                if (Boolean.TryParse(booleanString, out result))
                    return result;
            return null;
        }

        /// <summary>
        /// To check if it possible to parse into boolean.
        /// </summary>
        /// <param name="booleanString">Text to convert</param>
        /// <returns>true or false</returns>
        /// <example>
        ///     <code>
        ///         string textToCheck="true";
        ///         bool result=textToCheck.IsParsableToBoolean();
        ///     </code>
        /// </example>
        /// Contributed by Mohammad Rahman
        public static bool IsParsableToBoolean(this string booleanString)
        {
            bool result;
            return Boolean.TryParse(booleanString, out result);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseItemWithFormatString"></param>
        /// <param name="itemToFormat"></param>
        /// <returns></returns>
        /// Contributed by Mohammad Rahman
        public static string FormatWith(this string baseItemWithFormatString, params string[] itemToFormat)
        {
            return string.Format(baseItemWithFormatString, itemToFormat);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="itemTo"></param>
        /// <returns></returns>
        /// Contributed by Mohammad Rahman
        public static string PrefixAndPostfixCurlyBrackets(this string itemTo)
        {
            return String.Concat("{", itemTo, "}");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseItem"></param>
        /// <param name="itemToAppend"></param>
        /// <param name="noTimesToAdd"></param>
        /// <returns></returns>
        /// Contributed by Mohammad Rahman
        public static string AppendNoOfTimes(this string baseItem, string itemToAppend, int noTimesToAdd)
        {
            while (noTimesToAdd-- != 0)
                baseItem = baseItem.JoinWith(itemToAppend);
            return baseItem;
        }
    }
}
